View Javadoc

1   // jq_Thread.java, created Mon Feb  5 23:23:21 2001 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Scheduler;
5   
6   import joeq.Allocator.ObjectLayout;
7   import joeq.Class.PrimordialClassLoader;
8   import joeq.Class.jq_Class;
9   import joeq.Class.jq_CompiledCode;
10  import joeq.Class.jq_DontAlign;
11  import joeq.Class.jq_InstanceField;
12  import joeq.Class.jq_InstanceMethod;
13  import joeq.Class.jq_NameAndDesc;
14  import joeq.Class.jq_Reference;
15  import joeq.Class.jq_StaticMethod;
16  import joeq.Main.jq;
17  import joeq.Memory.CodeAddress;
18  import joeq.Memory.HeapAddress;
19  import joeq.Memory.StackAddress;
20  import joeq.Runtime.SystemInterface;
21  import joeq.Runtime.Unsafe;
22  import joeq.UTF.Utf8;
23  import jwutil.sync.AtomicCounter;
24  import jwutil.util.Assert;
25  
26  /***
27   * A jq_Thread corresponds to a Java (lightweight) thread.
28   * 
29   * @author  John Whaley <jwhaley@alum.mit.edu>
30   * @version $Id: jq_Thread.java 2466 2006-06-07 23:12:58Z joewhaley $
31   */
32  public class jq_Thread implements jq_DontAlign {
33  
34      // C code relies on this field being first.
35      private final jq_RegisterState registers;
36      // C code relies on this field being second.
37      private volatile int thread_switch_enabled;
38      // C code relies on this field being third.
39      private jq_NativeThread native_thread;
40      private final Thread thread_object;
41      jq_Thread next;
42      private jq_CompiledCode entry_point;
43      private boolean isDaemon;
44      boolean isScheduler;
45      private boolean hasStarted;
46      private boolean isDead;
47      boolean wasPreempted;
48      private int priority;
49      private volatile int isInterrupted;
50      private final int thread_id;
51      //  thread is in middle of delivering exception
52      public volatile boolean is_delivering_exception;
53      
54      public static int INITIAL_STACK_SIZE = 40960;
55      // two threads can be created at the same time
56      public static AtomicCounter thread_id_factory = new AtomicCounter(1);
57  
58      public jq_Thread(Thread t) {
59          this.thread_object = t;  // interface to java program
60          this.registers = jq_RegisterState.create();
61          this.thread_id = thread_id_factory.increment() << ObjectLayout.THREAD_ID_SHIFT;
62          Assert._assert(this.thread_id > 0);
63          Assert._assert(this.thread_id < ObjectLayout.THREAD_ID_MASK);
64          this.isDead = true; // threads start as dead.
65          this.priority = 5;
66      }
67  
68      public Thread getJavaLangThreadObject() { return thread_object; }
69      public String toString() { return thread_object + " (id="+thread_id+", sus: " + thread_switch_enabled+")"; }  //print out info for debugging
70      public jq_RegisterState getRegisterState() { return registers; }
71      public jq_NativeThread getNativeThread() { return native_thread; }
72      void setNativeThread(jq_NativeThread nt) { native_thread = nt; }
73      public boolean isThreadSwitchEnabled() { return thread_switch_enabled == 0; }
74      public void disableThreadSwitch() {
75          if (!jq.RunningNative) {
76              ++thread_switch_enabled; 
77          } else {
78              ((HeapAddress)HeapAddress.addressOf(this).offset(_thread_switch_enabled.getOffset())).atomicAdd(1);
79          }
80      }
81      public void enableThreadSwitch() {
82          if (!jq.RunningNative) {
83              --thread_switch_enabled;
84          } else {
85              ((HeapAddress)HeapAddress.addressOf(this).offset(_thread_switch_enabled.getOffset())).atomicSub(1);
86          }
87      }
88      
89      public void init() {
90          Thread t = thread_object;
91          jq_Reference z = jq_Reference.getTypeOf(t);
92          jq_InstanceMethod m = z.getVirtualMethod(new jq_NameAndDesc(Utf8.get("run"), Utf8.get("()V")));
93          entry_point = m.getDefaultCompiledVersion();
94          // initialize register state to start at start function
95          StackAddress stack = SystemInterface.allocate_stack(INITIAL_STACK_SIZE);
96          if (stack.isNull()) {
97              throw new OutOfMemoryError("Cannot allocate thread stack of size "+INITIAL_STACK_SIZE);
98          }
99          this.registers.setEsp(stack);
100         this.registers.setEip(entry_point.getEntrypoint());
101         // bogus return address
102         this.registers.setEsp((StackAddress) this.registers.getEsp().offset(-CodeAddress.size()));
103         // arg to run()
104         this.registers.setEsp((StackAddress) this.registers.getEsp().offset(-HeapAddress.size()));
105         this.registers.getEsp().poke(HeapAddress.addressOf(t));
106         // return from run() directly to destroy()
107         this.registers.setEsp((StackAddress) this.registers.getEsp().offset(-CodeAddress.size()));
108         this.registers.getEsp().poke(_destroyCurrentThread.getDefaultCompiledVersion().getEntrypoint());
109     }
110     public void start() {
111         if (entry_point == null) {
112             // java.lang.Thread objects in the boot image may not be initialized.
113             this.init();
114         }
115         if (this.hasStarted)
116             throw new IllegalThreadStateException();  // prevent to start twice
117         this.isDead = false;
118         this.hasStarted = true;
119         jq_NativeThread.startJavaThread(this);
120     }
121     long sleepUntil;
122     public void sleep(long millis) throws InterruptedException {
123         sleepUntil = System.currentTimeMillis() + millis;
124         for (;;) {
125             if (this.isInterrupted(true)) {
126                 throw new InterruptedException();
127             }
128             yield();
129             if (System.currentTimeMillis() >= sleepUntil) {
130                 break;
131             }
132         }
133     }
134     public void yield() {
135         if (this != Unsafe.getThreadBlock()) {
136             SystemInterface.debugwriteln("Yield called on " + this + " from thread " + Unsafe.getThreadBlock());
137             Assert.UNREACHABLE();
138         }
139         // act like we received a timer tick
140         this.disableThreadSwitch();
141         // store the register state to make it look like we received a timer tick.
142         StackAddress esp = StackAddress.getStackPointer();
143         // leave room for object pointer and return address
144         registers.setEsp((StackAddress) esp.offset(-CodeAddress.size()-HeapAddress.size()));
145         registers.setEbp(StackAddress.getBasePointer());
146         registers.setControlWord(0x027f);
147         registers.setStatusWord(0x4000);
148         registers.setTagWord(0xffff);
149         // other registers don't matter.
150         this.getNativeThread().yieldCurrentThread();
151     }
152     public void yieldTo(jq_Thread t) {
153         Assert._assert(this == Unsafe.getThreadBlock());
154         // if that thread is in the thread queue for the current native
155         // thread, we can yield to him easily.
156         this.disableThreadSwitch();
157         // thread switching for this native thread is disabled, so
158         // Java threads cannot move from our local thread queue.
159         if (t.getNativeThread() != this.getNativeThread()) {
160             // TODO: temporarily increase priority of t (?)
161             return;
162         }
163 
164         // act like we received a timer tick.
165         // store the register state to make it look like we received a timer tick.
166         StackAddress esp = StackAddress.getStackPointer();
167         // leave room for object pointer, arg, return address
168         registers.setEsp((StackAddress) esp.offset(-CodeAddress.size()-HeapAddress.size()-HeapAddress.size()));
169         registers.setEbp(StackAddress.getBasePointer());
170         registers.setControlWord(0x027f);
171         registers.setStatusWord(0x4000);
172         registers.setTagWord(0xffff);
173         // other registers don't matter.
174         this.getNativeThread().yieldCurrentThreadTo(t);
175     }
176     public void setPriority(int newPriority) {
177         Assert._assert(newPriority >= 0);
178         Assert._assert(newPriority <= 9);
179         this.priority = newPriority;
180     }
181     public int getPriority() {
182         return this.priority;
183     }
184     public void stop(Object o) { }
185     public void suspend() { }
186     public void resume() { }
187     public void interrupt() { this.isInterrupted = 1; }
188     public boolean isInterrupted(boolean clear) {
189         boolean isInt = this.isInterrupted != 0;
190         if (clear && isInt) {
191             //int res = Unsafe.atomicCas4(_isInterrupted.getAddress(), 1, 0);
192             this.isInterrupted = 0;
193         }
194         return isInt;
195     }
196     public boolean isAlive() { return !isDead; }
197     public boolean isDaemon() { return isDaemon; }
198     public void setDaemon(boolean b) { isDaemon = b; }
199     public int countStackFrames() { return 0; }
200     public int getThreadId() { return thread_id; }
201 
202     public static void destroyCurrentThread() {
203         jq_Thread t = Unsafe.getThreadBlock();
204         //Debug.writeln("Destroying thread ", t.getThreadId());
205         t.disableThreadSwitch();
206         t.isDead = true;
207         jq_NativeThread.endCurrentJavaThread();
208         Assert.UNREACHABLE();
209     }
210     
211     public jq_Thread getNext() {
212         return next;
213     }
214 
215     public static final jq_Class _class;
216     public static final jq_StaticMethod _destroyCurrentThread;
217     public static final jq_InstanceField _thread_switch_enabled;
218     public static final jq_InstanceField _isInterrupted;
219     static {
220         _class = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("Ljoeq/Scheduler/jq_Thread;");
221         _destroyCurrentThread = _class.getOrCreateStaticMethod("destroyCurrentThread", "()V");
222         _thread_switch_enabled = _class.getOrCreateInstanceField("thread_switch_enabled", "I");
223         _isInterrupted = _class.getOrCreateInstanceField("isInterrupted", "I");
224     }
225 }